package com.core.okhttpcore;

import okhttp3.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * http操作基类
 * 苏斌
 * 2022-4-8  * 增加代理配置
 * 2023-3-20  * 增加多实例map注册
 */
public class OkHttpUtil {

    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    private static final MediaType XML = MediaType.parse("application/xml; charset=utf-8");
    Logger log  = LoggerFactory.getLogger(this.getClass());

    private OkHttpClient okHttpClient;

    /**
     * 构造函数
     *
     * @param _okHttpClient
     */
    public OkHttpUtil(OkHttpClient _okHttpClient) {
        okHttpClient = _okHttpClient;
    }

    /**
     * 构造函数
     */
    public OkHttpUtil()
    {

        Config config = new Config();
        okHttpClient = intOkHttpClient(config);
    }

    /**
     * 构造函数
     *
     * @param config
     */
    public OkHttpUtil(Config config) {
        okHttpClient = intOkHttpClient(config);
    }



    protected OkHttpClient intOkHttpClient(Config config) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        //2022-7-15 重要： 当连接失败，尝试重连。组建默认保持5分钟连接，如果下一次连接还是相同url会默认调用原来的连接，但是如果对方客户端已经关闭
        //就会频繁出现  java.io.IOException: unexpected end of stream on ... 的错误
        builder.retryOnConnectionFailure(true);
        builder.connectionPool(pool(config.getPoolMaxIdleConnections(),config.getPoolKeepAlive()));

        builder.connectTimeout(config.getConnectTimeout(), TimeUnit.SECONDS);
        builder.readTimeout(config.getReadTimeout(), TimeUnit.SECONDS);
        builder.writeTimeout(config.getWriteTimeout(), TimeUnit.SECONDS);

        if (config.isSsl())//启用https
            builder.sslSocketFactory(sslSocketFactory(), x509TrustManager());//开启HTTPS

        if(config.isProxy())
        {
            //启动用代理
            builder.proxy(new Proxy(config.getType().equals("socks") ? Proxy.Type.SOCKS : Proxy.Type.HTTP, new InetSocketAddress(config.getHost(), config.getPort())));
        }

        OkHttpClient _okHttpClient = builder.build();
        return _okHttpClient;
    }

    public ConnectionPool pool(int poolMaxIdleConnections,int poolKeepAlive) {
        return new ConnectionPool(Integer.MAX_VALUE, poolKeepAlive, TimeUnit.MINUTES);
    }

    protected SSLSocketFactory sslSocketFactory() {
        try {
            // 信任任何链接
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
    }

    /**
     * get 请求
     *
     * @param url 请求url地址
     * @return string
     */
    public String doGet(String url) {
        return doGet(url, null, null);
    }


    /**
     * get 请求
     *
     * @param url    请求url地址
     * @param params 请求参数 map
     * @return string
     */
    public String doGet(String url, Map<String, String> params) {
        return doGet(url, params, null);
    }

    /**
     * get 请求
     *
     * @param url     请求url地址
     * @param headers 请求头字段 {k1, v1 k2, v2, ...}
     * @return string
     */
    public String doGet(String url, String[] headers) {
        return doGet(url, null, headers);
    }


    /**
     * get 请求
     *
     * @param url     请求url地址
     * @param params  请求参数 map
     * @param headers 请求头字段 {k1, v1 k2, v2, ...}
     * @return string
     */
    public String doGet(String url, Map<String, String> params, String[] headers) {

        StringBuilder sb = new StringBuilder(url);
        if (params != null && params.keySet().size() > 0) {
            boolean firstFlag = true;
            for (String key : params.keySet()) {
                if (firstFlag) {
                    sb.append("?").append(key).append("=").append(params.get(key));
                    firstFlag = false;
                } else {
                    sb.append("&").append(key).append("=").append(params.get(key));
                }
            }
        }
//---------------------------------------------
        Request.Builder builder = new Request.Builder();
        if (headers != null && headers.length > 0) {
            if (headers.length % 2 == 0) {
                for (int i = 0; i < headers.length; i = i + 2) {
                    builder.addHeader(headers[i], headers[i + 1]);
                }
            } else {
                log.warn("headers's length[{}] is error.", headers.length);
            }

        }
//-----------------------------------------------
        Request request = builder.url(sb.toString()).get().build();

        log.debug("do get request and url[{}]", sb.toString());
        String responseBody = "";
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            log.error("okhttp3 put error >> ex = {}", e.toString());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
        // return execute(request);
    }

    /**
     * post 请求
     *
     * @param url    请求url地址
     * @param params 请求参数 map
     * @return string
     */
    public String doPost(String url, Map<String, String> params) {
        return doPost(url, params, null);
    }

    /**
     * post 请求
     *
     * @param url     请求url地址
     * @param params  请求参数 map
     * @param headers 请求头字段 {k1, v1 k2, v2, ...}
     * @return
     */
    public String doPost(String url, Map<String, String> params, String[] headers) {
        FormBody.Builder formBody = new FormBody.Builder();
        if (params != null && params.keySet().size() > 0) {
            for (String key : params.keySet()) {
                formBody.add(key, params.get(key));
            }
        }

        //-------------------------------------------------

        Request.Builder builder = new Request.Builder();
        if (headers != null && headers.length > 0) {
            if (headers.length % 2 == 0) {
                for (int i = 0; i < headers.length; i = i + 2) {
                    builder.addHeader(headers[i], headers[i + 1]);
                }
            } else {
                log.warn("headers's length[{}] is error.", headers.length);
            }

        }
        //-----------------------------------------------

        // Request request = new Request.Builder().url(url).post(builder.build()).build();
        Request request = builder.url(url).post(formBody.build()).build();


        log.debug("do post request and url[{}]", url);

        String responseBody = "";
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            log.error("okhttp3 post error >> ex = {}", e.toString());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
    }

    /**
     * post 请求, 请求数据为 json 的字符串
     *
     * @param url     post 请求, 请求数据为 json 的字符串
     * @param json    请求url地址
     * @param headers 请求头字段 {k1, v1 k2, v2, ...}
     * @return
     */
    public String doPostJson(String url, String json, String[] headers) {
        //---------------------------------------------------
        RequestBody requestBody = RequestBody.Companion.create(json, JSON);
        //-------------------------------------------------
        Request.Builder builder = new Request.Builder();
        if (headers != null && headers.length > 0) {
            if (headers.length % 2 == 0) {
                for (int i = 0; i < headers.length; i = i + 2) {
                    builder.addHeader(headers[i], headers[i + 1]);
                }
            } else {
                log.warn("headers's length[{}] is error.", headers.length);
            }
        }
        //-----------------------------------------------
        Request request = builder.url(url).post(requestBody).build();

        log.debug("do post request and url[{}]", url);

        String responseBody = "";
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            log.error("okhttp3 post error >> ex = {}", e.toString());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
    }

    /**
     * post 请求, 请求数据为 xml 的字符串
     *
     * @param url     请求url地址
     * @param xml     请求数据, xml 字符串
     * @param headers headers 请求头字段 {k1, v1 k2, v2, ...}
     * @return
     */
    public String doPostXml(String url, String xml, String[] headers) {
        //---------------------------------------------------
        RequestBody requestBody = RequestBody.Companion.create(xml, XML);
        //-------------------------------------------------
        Request.Builder builder = new Request.Builder();
        if (headers != null && headers.length > 0) {
            if (headers.length % 2 == 0) {
                for (int i = 0; i < headers.length; i = i + 2) {
                    builder.addHeader(headers[i], headers[i + 1]);
                }
            } else {
                log.warn("headers's length[{}] is error.", headers.length);
            }
        }
        //-----------------------------------------------

        // Request request = new Request.Builder().url(url).post(builder.build()).build();
        Request request = builder.url(url).post(requestBody).build();

        log.debug("do post request and url[{}]", url);

        String responseBody = "";
        Response response = null;
        try {
            response = okHttpClient.newCall(request).execute();
            int status = response.code();
            if (response.isSuccessful()) {
                return response.body().string();
            }
        } catch (Exception e) {
            log.error("okhttp3 post error >> ex = {}", e.toString());
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return responseBody;
    }

}
